import inspect
import json
from datetime import datetime
from fastapi_async_sqlalchemy import db
from typing import Literal, Optional
from functools import wraps
import time
import os
from fastapi import Request
from user_agents import parse

from crud.admin_cruds.login_log_crud import crud_login_log
from crud.admin_cruds.oper_log_crud import crud_oper_log
from middleware.access_middle import req_ctx
from utils.common_helper import get_ip_location
from conf.config import BusinessType


class LogHelper:
    """辅助类，封装日志相关的辅助方法"""

    @staticmethod
    def get_relative_func_path(func) -> str:
        """获取函数的相对路径"""
        file_path = inspect.getfile(func)
        project_root = os.getcwd()
        relative_path = os.path.relpath(file_path, start=project_root)[:-3].replace('\\', '.')
        return f"{relative_path}{func.__name__}()"

    @staticmethod
    def get_operator_type(user_agent: str) -> int:
        """根据User-Agent判断操作类型"""
        if any(device in user_agent for device in ('Mobile', 'Android', 'iPhone')):
            return 2  # 移动设备
        if any(os in user_agent for os in ('Windows', 'Macintosh', 'Linux')):
            return 1  # 桌面设备
        return 0  # 未知

    @staticmethod
    async def get_request_payload(request: Request, kwargs: dict) -> str:
        """获取请求参数（异步方法）"""
        content_type = request.headers.get('Content-Type', '')

        if any(mime in content_type for mime in ('multipart/form-data', 'application/x-www-form-urlencoded')):
            form_data = kwargs.get('form_data', {})
            return '\n'.join(f"{key}: {value}" for key, value in form_data.__dict__.items())

        payload = {}
        if request_body := await request.body():
            payload.update(json.loads(request_body.decode('utf-8')))
        if path_params := request.path_params:
            payload.update(path_params)

        result = json.dumps(payload, ensure_ascii=False)
        return result if len(result) <= 2000 else "请求参数过长"


def sys_log(
        title: str,
        business_type: BusinessType,
        log_type: Literal['login', 'operation'] = 'operation'
):
    """
    日志装饰器
    :param title: 日志标题
    :param business_type: 业务类型
    :param log_type: 日志类型，login表示登录日志，operation表示操作日志
    """

    def decorator(func):
        @wraps(func)
        async def wrapper(*args, **kwargs):
            request: Request = req_ctx.get()
            start_time = time.time()

            # 收集基本信息
            user_agent = request.headers.get('User-Agent', '')
            oper_ip = request.headers.get('X-Forwarded-For') or request.client.host
            oper_location = get_ip_location(oper_ip)
            oper_time = datetime.now()

            # 处理请求参数（注意这里加了await）
            oper_param = await LogHelper.get_request_payload(request, kwargs)

            # 执行被装饰函数
            is_exception, exception = False, None
            try:
                response = await func(*args, **kwargs)
            except Exception as e:
                is_exception = True
                exception = e
                response = None

            # 记录日志
            if log_type == 'login':
                await _log_login(
                    request, kwargs, user_agent, oper_ip,
                    oper_location, oper_time, is_exception, exception
                )
            else:
                await _log_operation(
                    request, title, business_type, oper_param,
                    oper_ip, oper_time, is_exception, exception
                )

            if is_exception:
                raise exception
            return response

        return wrapper

    return decorator


async def _log_login(
        request: Request,
        kwargs: dict,
        user_agent: str,
        ip: str,
        location: str,
        oper_time: datetime,
        is_exception: bool,
        exception: Optional[Exception]
):
    """记录登录日志"""
    user_agent_info = parse(user_agent)
    browser = user_agent_info.browser.family
    os_name = user_agent_info.os.family

    if user_agent_info.browser.version:
        browser += f" {user_agent_info.browser.version[0]}"
    if user_agent_info.os.version:
        os_name += f" {user_agent_info.os.version[0]}"

    await crud_login_log.create(obj_in={
        "username": (kwargs.get('login_in') and kwargs.get('login_in').username) or
                    (kwargs.get('form_data') and kwargs.get('form_data').username),
        "ip_address": ip,
        "login_location": location,
        "browser": browser,
        "os": os_name,
        "status": not is_exception,
        "msg": getattr(exception, 'msg', 'Success'),
        "login_time": oper_time.strftime('%Y-%m-%d %H:%M:%S')
    })
    await db.session.commit()


async def _log_operation(
        request: Request,
        title: str,
        business_type: BusinessType,
        oper_param: str,
        ip: str,
        oper_time: datetime,
        is_exception: bool,
        exception: Optional[Exception]
):
    """记录操作日志"""
    current_user = request.state.user
    await crud_oper_log.create(obj_in={
        "title": title,
        "business_type": business_type,
        "user_id": current_user.id,
        "username": current_user.username,
        "method": request.method,
        "path": request.url.path,
        "ip": ip,
        "args": oper_param,
        "message": getattr(exception, 'msg', 'Success'),
    })
    await db.session.commit()